﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Configuration;
using System.Data.SqlClient;
using AutoWizard.Data;
using AutoWizard.Meta;
using System.Text.RegularExpressions;

namespace AutoWizard.Dialog
{
    public partial class DBExportForm : Form
    {
        public DBExportForm( string path )
        {
            CurrentPath = path;
            InitializeComponent();
        }


        #region CurrentPath

        string m_CurrentPath = "";

        public string CurrentPath
        {
            get { return m_CurrentPath; }
            set { m_CurrentPath = value; }
        }
        #endregion

        #region Form_Load

        private void DBExportForm_Load(object sender, EventArgs e)
        {
            txtServer.Text = ConfigurationManager.AppSettings["Server"];
            txtDB.Text = ConfigurationManager.AppSettings["Data Source"];
            txtUser.Text = ConfigurationManager.AppSettings["User ID"];
            txtPwd.Text = ConfigurationManager.AppSettings["Password"];

            comboBox1.SelectedIndex = 0;
        }

        #endregion

        #region Cancel Click

        private void button2_Click(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }

        #endregion

        #region OK Click

        private void button1_Click(object sender, EventArgs e)
        {

            using (SqlConnection con = new SqlConnection(GetConnectString()))
            {
                con.Open();
                string strSql = string.Format("{0};{1};{2}" ,
                    GDB.GetTableSQL(GetDBType()), 
                    GDB.GetForigenKeySQL(GetDBType()),
                    GDB.GetDomSQL(GetDBType()) );

                SqlCommand cmd = new SqlCommand(strSql, con);
                SqlDataAdapter sda = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                sda.Fill(ds);

                // 获得XmlContent字段内容，反序列化为BusinessFunction对象
                if (ds.Tables.Count == 3)
                {
                    // 分析两个表的数据，建立表对象模型
                    DataTable2EntityMetas(ds.Tables[0], ds.Tables[1] , ds.Tables[2] );
                }
            }

            this.DialogResult = DialogResult.OK;
            this.Close();

        }

        #endregion

        #region GetConnectString

        private string GetConnectString()
        {
            return string.Format("data source={0};user id={1};password={2};initial catalog={3};Persist Security Info=true" ,
                txtServer.Text.Trim(), txtUser.Text.Trim(), txtPwd.Text.Trim(), txtDB.Text.Trim() );
        }
        #endregion

        #region GetDBType

        private DBType GetDBType()
        {
            if (comboBox1.SelectedItem.Equals("SQL2000"))
                return DBType.SQL2000;
            return DBType.SQL2005;
        }
        #endregion

        #region EntityMetas

        List<EntityMeta> m_metas = new List<EntityMeta>();

        public List<EntityMeta> EntityMetas
        {
            get { return m_metas; }
        }

        #endregion

        #region DataTable2EntityMetas

        private void DataTable2EntityMetas(DataTable dtTable, DataTable dtForigen , DataTable dtDom )
        {
            // 建立外键关联字典
            Dictionary<string, string> dictRel = new Dictionary<string,string>();
            Dictionary<string, List<string>> dictFkey = new Dictionary<string, List<string>>();

            GetForigenDictionary(dtForigen, dictRel, dictFkey);

            Dictionary<string, string> dictDom = GetDomDictionary(dtDom);
            // 建立实体字典
            Dictionary<string, EntityMeta> dictMeta = new Dictionary<string, EntityMeta>();
            for (int i = 0; i < dtTable.Rows.Count; i++)
            {
                DataRow dRow = dtTable.Rows[i];

                // 判断表名是否需要新建对象
                // TableName,FieldName,IsKey,FieldType,Length,FieldPrecision,
                // Scale,CanNull,DefaultValue,DescInfo
                string tableName = dRow["TableName"].ToString().Trim();

                EntityMeta meta = new EntityMeta(tableName);
                if (dictMeta.ContainsKey(tableName))
                    meta = dictMeta[tableName];
                else
                    dictMeta.Add(tableName, meta);

                SettingEntityMeta(meta);
                
                // 建立一个字段的定义
                PropertyMeta proMeta = new PropertyMeta(dRow["FieldName"].ToString().Trim());
                proMeta.IsKey = Convert.ToBoolean(Convert.ToInt32(dRow["IsKey"]));

                if (proMeta.IsKey)
                    meta.KeyField = proMeta.Name;

                // 将可能的用户类型(如DOM_ID)转换为系统类型
                string fieldType = DomConvert( dictDom , dRow["FieldType"].ToString().Trim());
                proMeta.DbType = GetDbType(fieldType);
                if (IsStringField(proMeta.DbType))
                    proMeta.TextLimitLength = Convert.ToInt32(dRow["Length"]);
                else if (IsMoneyField(proMeta.DbType))
                {
                    proMeta.MoneyPrecision = Convert.ToInt32(dRow["FieldPrecision"]);
                    proMeta.MoneyScale = Convert.ToInt32(dRow["Scale"]);
                }
                proMeta.CanNull = Convert.ToBoolean(Convert.ToInt32(dRow["CanNull"]));
                proMeta.DescInfo = dRow["DescInfo"].ToString().Trim();
                proMeta.DefaultValue = GetDefaultValue( proMeta.DbType , dRow["DefaultValue"].ToString());

                // 判断该字段是否外键字段，增加相应属性
                string key = string.Format("{0}.{1}", tableName, proMeta.Name);
                if (dictRel.ContainsKey(key))
                {
                    string str = dictRel[key];
                    int index = str.IndexOf(".");
                    string fTable = str.Substring(0, index);
                    string fField = str.Substring(index + 1);
                    proMeta.IsForigenKey = true;
                    proMeta.MasterTable = fTable;
                    proMeta.MasterField = fField;
                }
                meta.Propertys.Add(proMeta);

                // 判断是否别人的外键，建立子集字段
                if (proMeta.IsKey)
                {
                    string fkey = string.Format("{0}.{1}", tableName, proMeta.Name);
                    if (dictFkey.ContainsKey(fkey))
                    {
                        foreach (string str in dictFkey[fkey])
                        {
                            int index = str.IndexOf(".");
                            string fTable = str.Substring(0, index);
                            string fField = str.Substring(index + 1);

                            PropertyMeta fMeta = new PropertyMeta( fTable+"s" );
                            fMeta.IsAssociation = true;
                            fMeta.DetailTable = fTable;

                            meta.Propertys.Add(fMeta);
                        }
                    }
                }

            }

            m_metas.Clear();
            m_metas.AddRange(dictMeta.Values);
        }

        #endregion

        #region GetForigenDictionary

        private void GetForigenDictionary(DataTable dtForigen, Dictionary<string, string> dictRel,
            Dictionary<string, List<string>> dictFKey)
        {
            for (int i = 0; i < dtForigen.Rows.Count; i++)
            {
                DataRow dRow = dtForigen.Rows[i];

                string detailTableName = dRow["TableName"].ToString().Trim();
                string detailForergnKey = dRow["ForergnKeyColumn"].ToString().Trim();
                string masterTableName = dRow["RefTableName"].ToString().Trim();
                string masterKeyName = dRow["RefColumnName"].ToString().Trim();

                string key = string.Format("{0}.{1}", detailTableName, detailForergnKey);
                string value = string.Format("{0}.{1}", masterTableName, masterKeyName);

                dictRel.Add(key, value);
                if (dictFKey.ContainsKey(value))
                {
                    dictFKey[value].Add(key);
                }
                else
                {
                    List<string> list = new List<string>();
                    list.Add(key);
                    dictFKey.Add(value, list);
                }
            }
        }

        #endregion

        #region GetDomDictionary

        private Dictionary<string, string> GetDomDictionary(DataTable dtDom)
        {
            Dictionary<string, string> dict = new Dictionary<string, string>();
            for (int i = 0; i < dtDom.Rows.Count; i++)
            {
                DataRow dRow = dtDom.Rows[i];

                dict.Add(dRow["DOMAIN_NAME"].ToString().Trim(), dRow["DATA_TYPE"].ToString().Trim());
            }

            return dict;
        }
        #endregion

        #region GetDefaultValue

        private string GetDefaultValue(SqlDbType dbType, string defaultValue)
        {
            if (string.IsNullOrEmpty(defaultValue.Trim()))
                return string.Empty;

            string ret = string.Empty;

            Regex regex;
            Match match;
            switch (dbType)
            {
                case SqlDbType.Binary :
                case SqlDbType.Image :
                case SqlDbType.NText :
                case SqlDbType.Text :
                case SqlDbType.Timestamp :
                case SqlDbType.VarBinary :
                case SqlDbType.UniqueIdentifier :
                    break;
                case SqlDbType.BigInt :
                case SqlDbType.Decimal :
                case SqlDbType.Float:
                case SqlDbType.Int :
                case SqlDbType.Money :
                case SqlDbType.Real :
                case SqlDbType.SmallInt :
                case SqlDbType.SmallMoney :
                case SqlDbType.TinyInt :
                    // ((1.2))
                    regex = new Regex( @"\(\((?<data>[^)])\)\)" , RegexOptions.IgnoreCase );
                    match = regex.Match(defaultValue.Trim());
                    if (match != null)
                    {
                        ret = match.Groups["data"].Value;
                    }
                    break;
                case SqlDbType.Bit :
                    // ((1))
                    regex = new Regex( @"\(\((?<data>[^)])\)\)" , RegexOptions.IgnoreCase );
                    match = regex.Match(defaultValue.Trim());
                    if (match != null)
                    {
                        switch (match.Groups["data"].Value)
                        {
                            case "0" :
                                ret = "false";
                                break;
                            case "1":
                                ret = "true" ;
                                break;
                        }
                    }
                    break;
                case SqlDbType.Char :
                case SqlDbType.VarChar :
                    // ('Monring')
                    regex = new Regex(@"\('(?<data>[^']+)'\)", RegexOptions.IgnoreCase);
                    match = regex.Match(defaultValue.Trim());
                    if (match != null)
                    {
                        ret = match.Groups["data"].Value;
                    }
                    break;
                case SqlDbType.NChar :
                case SqlDbType.NVarChar :
                    // (N'HELLO')
                    regex = new Regex(@"\(N'(?<data>[^']+)'\)", RegexOptions.IgnoreCase);
                    match = regex.Match(defaultValue.Trim());
                    if (match != null)
                    {
                        ret = match.Groups["data"].Value;
                    }
                    break;
                case SqlDbType.DateTime :
                case SqlDbType.SmallDateTime :
                    ret = DateTime.MinValue.ToString();
                    break;
            }
            
            return ret;
        }

        #endregion

        #region DomConvert

        private string DomConvert(Dictionary<string, string> dictDom, string fieldType)
        {
            if (dictDom.ContainsKey(fieldType))
                return dictDom[fieldType];

            return fieldType;
        }

        #endregion

        #region GetDbType

        private SqlDbType GetDbType(string dbType)
        {
            SqlDbType retType = SqlDbType.Char;
            switch (dbType)
            {
                case "bigint": // System.Int64
                    retType = SqlDbType.BigInt;
                    break;
                case "binary": // System.Array of Systme.Byte
                    retType = SqlDbType.Binary;
                    break;
                case "bit":    // System.Boolean
                    retType = SqlDbType.Bit;
                    break;
                case "char":   // System.String  1-8000
                    retType = SqlDbType.Char;
                    break;
                case "datetime": // System.DateTime
                    retType = SqlDbType.DateTime;
                    break;
                case "decimal":  // Systme.Decimal
                    retType = SqlDbType.Decimal;
                    break;
                case "float":   // System.Double
                    retType = SqlDbType.Float;
                    break;
                case "image":   // System.Array of Systme.Byte
                    retType = SqlDbType.Image;
                    break;
                case "int":     // System.Int32
                    retType = SqlDbType.Int;
                    break;
                case "money":   // System.Decimal
                    retType = SqlDbType.Money;
                    break;
                case "nchar":   // System.String  1-4000
                    retType = SqlDbType.NChar;
                    break;
                case "ntext":   // Unlimit Size System.String
                    retType = SqlDbType.NText;
                    break;
                case "numeric": // System.Decimal
                    retType = SqlDbType.Decimal ;
                    break;
                case "nvarchar": // System.String 1-4000
                    retType = SqlDbType.NVarChar;
                    break;
                case "real":    // System.Single
                    retType = SqlDbType.Real;
                    break;
                case "smalldatetime":   // System.DateTime
                    retType = SqlDbType.DateTime;
                    break;
                case "smallint":        // System.Int16
                    retType = SqlDbType.SmallInt;
                    break;
                case "smallmoney":      // System.Decimal
                    retType = SqlDbType.SmallMoney;
                    break;
                case "sql_variant":     // System.Object
                    retType = SqlDbType.Variant;
                    break;
                case "text":   // Unlimit Size System.String
                    retType = SqlDbType.Text;
                    break;
                case "timestamp":       // System.Array of Systme.Byte 8
                    retType = SqlDbType.Timestamp;
                    break;
                case "tinyint":     // System.Byte
                    retType = SqlDbType.TinyInt;
                    break;
                case "uniqueidentifier":    // System.Guid
                    retType = SqlDbType.UniqueIdentifier;
                    break;
                case "varbinary": //  System.Array of Systme.Byte 1-8000 8000以上要指定
                    retType = SqlDbType.VarBinary;
                    break;
                case "varchar":     // System.String 1-8000
                    retType = SqlDbType.VarChar;
                    break;
                case "xml":     // System.String
                    retType = SqlDbType.Xml;
                    break;
            }

            return retType;
        }
        #endregion

        #region IsStringField

        private bool IsStringField(SqlDbType dbType)
        {
            bool bRet = false;
            switch ( dbType )
            {
                case SqlDbType.Char :
                case SqlDbType.NChar:
                case SqlDbType.NVarChar:
                case SqlDbType.VarChar:
                    bRet = true ;
                    break;
            }

            return bRet;
        }
        #endregion

        #region IsMoneyField

        private bool IsMoneyField(SqlDbType dbType)
        {
            bool bRet = false;
            switch ( dbType )
            {
                case SqlDbType.Decimal:
                case SqlDbType.Money:
                case SqlDbType.SmallMoney:
                    bRet = true ;
                    break;
            }

            return bRet;
        }
        #endregion

        #region SettingEntityMeta
        
        private void SettingEntityMeta(EntityMeta meta)
        {
            meta.GenConfig.MdlConfig.Using = ConfigurationManager.AppSettings["MdlUsing"];
            meta.GenConfig.MdlConfig.Namespace = ConfigurationManager.AppSettings["MdlNameSpace"];
            meta.GenConfig.MdlConfig.ClassName = string.Format(ConfigurationManager.AppSettings["MdlClassNameFormat"], meta.TableName);
            meta.GenConfig.DalConfig.Using = ConfigurationManager.AppSettings["DalUsing"];
            meta.GenConfig.DalConfig.Namespace = ConfigurationManager.AppSettings["DalNameSpace"];
            meta.GenConfig.DalConfig.ClassName = string.Format(ConfigurationManager.AppSettings["DalClassNameFormat"], meta.TableName);
            meta.GenConfig.BllConfig.Using = ConfigurationManager.AppSettings["BllUsing"];
            meta.GenConfig.BllConfig.Namespace = ConfigurationManager.AppSettings["BllNameSpace"];
            meta.GenConfig.BllConfig.ClassName = string.Format(ConfigurationManager.AppSettings["BllClassNameFormat"], meta.TableName);

        }

        #endregion
    }
}